/*
 ********************************************************************************
 *                      桂林聚联科技有限公司
 *                           Copyright
 *  文件描述 ： 
 *
 *
 *  文件名称 ： OtdrMain.c
 *  创建者   ： 
 *  创建日期 ： 2016-10-25 17:01:44
 *  当前版本 ： 
 ********************************************************************************
*/
#include <stdio.h>
#include <assert.h>
#include <math.h>

#include "prototypes.h"
#include "OtdrSpiData.h"
#include "otdrtestinterface.h"

#define OtdrSleepTime   15000

/******************************** some function declare ************************************/
int get_one_piece_of_data(DeviceInfo_t *device, FpgaStartParam_t *fsp, OtdrSpiData_t *osd, OtdrStateCtrl_t *StateCtrl, int (*stop)(void));

#if 0
/*
 **************************************************************************************************
 *  函数  名： StartTest
 *  函数描述： 
 *  入口参数： 
 *  返回参数： 
 *  日期版本： 2016年 10月 20日 星期四 15:21:00 CST
 **************************************************************************************************
*/
int startTest(OtdrTestParam_t *tp, 
            int (*ResultCopyBack)(OtdrTestResult_t *, OtdrTestResult_t *, int), \
            OtdrTestResult_t *OtdrTestResult, \
            int (*stop)(void), \
            DeviceInfo_t *device)
{
#define FPGA_PERIOD 1000    // starts fpga at every 1000ms
    char  timebuf[32];
	int   i, FpgaStartCount, RefreshCount, err_no, datamask;
    int   rcv, power, apdv;
	int   PeriodTime, TotalMeasureTime;
	int   LowPowerTime, HighPowerTime;

    OtdrSpiData_t   osd;
    OtdrData_t      OtdrData;
    FpgaStartParam_t fsp;

    OtdrTestResult_t result;
    OtdrStateCtrl_t OtdrStateCtrl;
    OtdrTestParam_t autoTP, *TestParam = (OtdrTestParam_t *)tp;
    ConcatParam_t ConcatParam;

    SaveRunningData_t srd;
	
    err_no = E_CMD_OK;
    printf("StartTest at time: %s\n", CurrentDateTime(timebuf));
    parsePrint();
    parseSRDconf  ("/mnt/udisk/TR600plus/config/saverunningdata.conf", &srd);
/******************************* Check OTDR Mode   *****************************************/
    if((OTDR_MODE_AVG == TestParam->OtdrMode) || (OTDR_MODE_REALTIME == TestParam->OtdrMode))
    {
        OtdrUpdateParam(&OtdrStateCtrl, TestParam);
    }
    else
    {
        m1_error("\nUnknow OtdrMode!!!!!!\n");
        err_no = E_TEST_PARAM;
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }

    if(CheckRunParamValid(&OtdrStateCtrl) == 0){
        m1_error("CheckRunParamValid failed, do NOTHING, returns\n");
        err_no = E_TEST_PARAM;
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
/**************************** Check AutoMeasure param ******************************/
    err_no = adaptTestParam(tp, &autoTP, stop, device);
    if(err_no != E_CMD_OK){
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
    else{
        OtdrUpdateParam(&OtdrStateCtrl, &autoTP);
        // copy automeasure parameters to host
        CopyAutoMeasureParam(&result, &autoTP);
        ResultCopyBack(OtdrTestResult, &result, err_no);
    }
/**************************** Check the concat param *******************************/
    err_no = getConcat(tp, &ConcatParam, stop, device);
    if(err_no != E_CMD_OK){
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
    memcpy(&OtdrStateCtrl.ConcatParam, &ConcatParam, sizeof(ConcatParam));

/********************************************************************************/
/******************** Parameters OK, StartTest **********************************/
/********************************************************************************/
    // memory alloc for SpiData and OtdrData
    err_no = SpiDataMalloc(&osd, OtdrStateCtrl.DataNum);
    err_no = OtdrDataMalloc(&OtdrData, OtdrStateCtrl.DataNum);
    err_no = OtdrTestResultMalloc(&result, OtdrStateCtrl.DataNum);
    if(err_no == E_MEM_ALLOC){
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
/***************************** Starts to Measure ***********************************/
    OtdrStateCtrl.RefreshCount = 0;
    SpiClrData(&osd);
    OtdrClrData(&OtdrData);

    // Starts fpga to test every 1000ms
    if(OtdrStateCtrl.OtdrMode == OTDR_MODE_AVG){
        FpgaStartCount = OtdrStateCtrl.MeasureParam.MeasureTime_ms / FPGA_PERIOD;
        RefreshCount   = OtdrStateCtrl.MeasureParam.MeasureTime_ms / OtdrStateCtrl.RefreshPeriod_ms - 1;
    }
    else if(OtdrStateCtrl.OtdrMode == OTDR_MODE_REALTIME){
        FpgaStartCount = 0x7fffffff;  // in realtime mode, let it be the largest integer
        RefreshCount   = 0x7fffffff;  // in realtime mode, let it be the largest integer
    }
    else{
        FpgaStartCount = 0;           // otherwise, let it be 0
        RefreshCount   = 0;           // otherwise, let it be 0
    }

    PeriodTime       = 0;  // measure time of this period
    TotalMeasureTime = 0;  // total measure time of this measure
    LowPowerTime     = 0;
    HighPowerTime    = 0;

    apdv = getApdV(&OtdrStateCtrl);
    for(i = 0; i < FpgaStartCount; i++)
    {
/****************************** StartFPGA to get data *********************************/
        GetReceiverAndPowerLevel(&OtdrStateCtrl, TotalMeasureTime, &rcv, &power);
        TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, power, rcv, apdv, FPGA_PERIOD);
        err_no = get_one_piece_of_data(device, &fsp, &osd, &OtdrStateCtrl, stop);
        if(err_no == E_CMD_OK){
            PeriodTime       += FPGA_PERIOD;
            TotalMeasureTime += FPGA_PERIOD;

            if(OtdrStateCtrl.TreatAsHighPowerData)  HighPowerTime += FPGA_PERIOD;
            else                                    LowPowerTime  += FPGA_PERIOD;
/****************************** process data if refresh *******************************/
            if((PeriodTime >= OtdrStateCtrl.RefreshPeriod_ms) && (RefreshCount > 0))
            {
                PeriodTime                     = 0;
                OtdrStateCtrl.TotalMeasureTime = TotalMeasureTime;
                OtdrStateCtrl.LowPowerTime     = LowPowerTime;
                OtdrStateCtrl.HighPowerTime    = HighPowerTime;

                datamask = (OtdrStateCtrl.TreatAsHighPowerData == 1) ? SPIDATA_MASK_HP : SPIDATA_MASK_LP;
                SpiCopyData(&OtdrData, &osd, datamask);
                OtdrStateCtrl.FindEvent = 0;
                m1_print("Starts ProcessRefreshData at time: %s\n", CurrentDateTime(timebuf));
                err_no = ProcessRefreshData(&OtdrData, &OtdrStateCtrl, &result);
                ResultCopyBack(OtdrTestResult, &result, err_no);
                RefreshCount--;
                m1_print("RefreshCount = %d\n", RefreshCount);

                if(OtdrStateCtrl.OtdrMode != OTDR_MODE_AVG){
                    LowPowerTime = 0;
                    HighPowerTime = 0;
                    TotalMeasureTime = 0;
                }
            }
        }
        else    break;  // stopped
    }
/**************************** data ready, final process ****************************/
    if((err_no == E_CMD_OK) || (err_no == E_STOPPED)){
        datamask = (OtdrStateCtrl.TreatAsHighPowerData == 1) ? SPIDATA_MASK_HP : SPIDATA_MASK_LP;
        SpiCopyData(&OtdrData, &osd, datamask);

        OtdrStateCtrl.TotalMeasureTime    = TotalMeasureTime;
        OtdrStateCtrl.LowPowerTime        = LowPowerTime;
        OtdrStateCtrl.HighPowerTime       = HighPowerTime;
        OtdrStateCtrl.FindEvent = 1;
        m1_print("StartTest finished at time: %s\n", CurrentDateTime(timebuf));
        err_no = ProcessFinalData(&OtdrData, &OtdrStateCtrl, &result, &srd);
        ResultCopyBack(OtdrTestResult, &result, err_no);
    }
    OtdrStateCtrl.OtdrMode = OTDR_MODE_IDLE;

    // free data
    SpiDataFree(&osd);
    OtdrDataFree(&OtdrData);
    OtdrTestResultFree(&result);
    writeSRD(&srd);
    return err_no;
}

#endif

/*
 **************************************************************************************************
 *  函数  名： adaptTestParam
 *  函数描述： Auto measure the fiberlen to adapt proper measurelength and pulsewidth
 *  入口参数： 
 *  返回参数： 
 *  日期版本： 2016年 10月 20日 星期四 15:22:12 CST
 **************************************************************************************************
*/
int adaptTestParam(OtdrTestParam_t *tp, \
            OtdrTestParam_t *retTp, \
            int (*stop)(void), \
            DeviceInfo_t *device)
{
    extern int ti, fi;
    char  timebuf[32], fn[3][40], param[64], fname[100];
	int   apdv, err_no = E_CMD_OK, measuretime = 400;
    int   save = 0, tryindex = 0, rcv = OtdrAutoMeasureParam[2][0], power = OtdrAutoMeasureParam[3][0];
    float fiberlen;
    FpgaStartParam_t fsp;

    OtdrSpiData_t   osd;
    OtdrData_t      OtdrData;

    OtdrStateCtrl_t OtdrStateCtrl;
    OtdrTestParam_t *TestParam = (OtdrTestParam_t *)tp;

    SaveRunningData_t srd;
    parsePrint();
    configOtdrAlgo("/opt/TR600plus/config/otdralgo_config", tp->DataNum);
    parseSRDconf  ("/mnt/udisk/TR600plus/config/saverunningdata.conf", &srd);
/**** check if it needs to save data ***************************/
    if(isSRDcode(&srd, AUTOMEASURE)){
        char  DATE_TIME[20];
        time_t ct = time(NULL);
        struct tm *p = localtime(&ct);

        save = 1;
        sprintf(DATE_TIME, "%04d%02d%02d_%02d%02d%02d", p->tm_year+1900, \
                                                        p->tm_mon+1, \
                                                        p->tm_mday, \
                                                        p->tm_hour, \
                                                        p->tm_min, \
                                                        p->tm_sec);
        sprintf(fn[0], "auto1_%s", DATE_TIME);
        sprintf(fn[1], "auto2_%s", DATE_TIME);
        sprintf(fn[2], "auto3_%s", DATE_TIME);
    }
/******************* Update Param and alloc ************************/
#if DATA_FILE
    if(tp->MeasureLength_m == 0){
        tp->MeasureLength_m = 30000;
        tp->PulseWidth_ns = 80;
    }
#endif
    OtdrUpdateParamAutoAdapt(&OtdrStateCtrl, (OtdrTestParam_t *)tp);
    if(TestParam->MeasureLength_m != 0)     goto _ADAPT_DONE_;

/*****************************************************************/
    printf("Start adaptTestParam at time: %s\n", CurrentDateTime(timebuf));
    // memory alloc for SpiData and OtdrData
    err_no = SpiDataMalloc(&osd, OtdrStateCtrl.DataNum);
    err_no = OtdrDataMalloc(&OtdrData, OtdrStateCtrl.DataNum);
    if(err_no == E_MEM_ALLOC){
        return err_no;
    }

    apdv = getApdV(NULL);
/*****************************************************************/
    for(tryindex = 0; tryindex < 3; tryindex++){
        ti = tryindex+1;    // debug only
        SpiClrData(&osd);
        OtdrClrData(&OtdrData);
        TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, power, rcv, apdv, measuretime);
        err_no = get_one_piece_of_data(device, &fsp, &osd, &OtdrStateCtrl, stop);
        if(err_no == E_CMD_OK)   SpiCopyData(&OtdrData, &osd, SPIDATA_MASK_HP);
        else    break;

        if(save){
            sprintf(param, "%dnm%dkm%dns%dsP%dR%XV%d", OtdrStateCtrl.MeasureParam.Lambda_nm, \
                                                       OtdrStateCtrl.MeasureParam.MeasureLength_m/1000, \
                                                       OtdrStateCtrl.MeasureParam.PulseWidth_ns, \
                                                       measuretime/1000, \
                                                       power, \
                                                       rcv, \
                                                       apdv);
            sprintf(fname, "%s_%s", fn[tryindex], param);
            putSRD(&srd, fname, param, AUTOMEASURE, OtdrData.hp, OtdrStateCtrl.DataNum);
        }

        OtdrStateCtrl.TotalMeasureTime = measuretime;
        fiberlen = EstimateFiberLen(&OtdrStateCtrl, &OtdrData);
        m1_print("Automeasure tryindex = %d : %.2fm, v = %.2f\n", tryindex, fiberlen, OtdrStateCtrl.AutoEndLevel);

        // if the fiberlen is too long for the shorter measurelength, then break the loop
        if(tryindex < 2){
            if(fiberlen * 1.5 >= OtdrAutoMeasureParam[0][tryindex+1])   break;
            else{
                OtdrStateCtrl.MeasureParam.MeasureLength_m = OtdrAutoMeasureParam[0][tryindex+1];
                OtdrStateCtrl.MeasureParam.PulseWidth_ns   = OtdrAutoMeasureParam[1][tryindex+1];
                rcv = OtdrAutoMeasureParam[2][tryindex+1];
                power = OtdrAutoMeasureParam[3][tryindex+1];

                // 根据光纤长度匹配采样率和脉冲周期
                AdaptSampleFreq_PulsePeriod(&OtdrStateCtrl);
                OtdrStateInit(&OtdrStateCtrl);
            }
        }
    }

    if(err_no == E_CMD_OK){
        AdaptMeasureLength_PulseWidth(fiberlen, &OtdrStateCtrl);
    }
    
    // free data
    SpiDataFree(&osd);
    OtdrDataFree(&OtdrData);
    writeSRD(&srd);

_ADAPT_DONE_:
    // specify the test parameters
    TestParam = (OtdrTestParam_t *)retTp;
    memcpy(TestParam, tp, sizeof(OtdrTestParam_t));
    TestParam->SampleRate_Hz = OtdrStateCtrl.RealSampleRate_Hz;
    TestParam->MeasureLength_m = OtdrStateCtrl.MeasureParam.MeasureLength_m;
    TestParam->PulseWidth_ns = OtdrStateCtrl.MeasureParam.PulseWidth_ns;

    return err_no;
}

/*
 ********************************************************************************
 *  函数名称： getConcat
 *  函数描述： measure to specify the concat mode, after return, it confirms the
 *             concat mode, concat point, rcv and power whether if it needs to concat
 *  入口参数： 
 *  返回参数： 
 *  日期版本： 2016-10-21 15:22:23
 ********************************************************************************
*/
int MeasureGetAutoPower(OtdrStateCtrl_t *StateCtrl, ConcatParam_t *cp, OtdrData_t *OtdrData, OtdrSpiData_t *osd, int apdv)
{
#if 0
    extern int CheckIfAutoPower(OtdrStateCtrl_t *StateCtrl);
#if TR600_A_CLASS
    int minPower = 1;
#else   // TR600_C_CLASS
    int minPower = 2;
#endif
    FpgaStartParam_t fsp;

    if(CheckIfAutoPower(StateCtrl))
    {
        extern int CheckIfFrontSaturate(OtdrData_t *OtdrData, OtdrStateCtrl_t *StateCtrl, int FrontLen)
        int  i, j, Rcv, FrontSat, n;
#if TR600_A_CLASS
        int power = 2;
#else   // TR600_C_CLASS
        int power = MAX_POWER_LEVEL;
#endif
        
        // start to measure
        while(power > minPower)
        {
            m1_print("MeasureGetAutoPower using RCV = %d with power = %d\n", cp->LowRcv, power);
            SpiClrData(&osd);
            OtdrClrData(&OtdrData);
            TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, cp->LowPower, cp->LowRcv, apdv, measuretime);
            err_no = get_one_piece_of_data(device, &fsp, &osd, &OtdrStateCtrl, stop);
            if(err_no == E_CMD_OK){
                SpiCopyData(&OtdrData, &osd, SPIDATA_MASK_HP);
                n = 4*PulseWidthInSampleNum();
                FrontSat = CheckIfFrontSaturate(&OtdrData, &OtdrStateCtrl, n);
                if(FrontSat)    power--;
                else            break;

            }
            else    break;
        }
        power = MAX(power, minPower);
        cp->LowPower = power;
    }
#endif
    return E_CMD_OK;
}

int getConcat(OtdrTestParam_t *tp, \
            ConcatParam_t *cp, \
            int (*stop)(void), \
            DeviceInfo_t *device)
{
    extern OtdrPowerLevel_t GetPowerLevelLPIndex(Uint32 Lambda);
    char timebuf[32];
	int   i, j, err_no = E_CMD_OK, measuretime = 400;
    int   lr, lp, hr, hp;
    int   apdv;
    OtdrPowerLevel_t    OtdrPowerLevel;
    OtdrPowerLevelLP_t  OtdrPowerLevel_LP;
    FpgaStartParam_t fsp;

    OtdrSpiData_t   osd;
    OtdrData_t      OtdrData;

    OtdrStateCtrl_t OtdrStateCtrl;
    OtdrTestParam_t *TestParam = (OtdrTestParam_t *)tp;
    ConcatParam_t ConcatParam;

    SaveRunningData_t srd;
    parsePrint();
    configOtdrAlgo("/opt/TR600plus/config/otdralgo_config", tp->DataNum);
    parseSRDconf  ("/mnt/udisk/TR600plus/config/saverunningdata.conf", &srd);
/******************* Update Param and alloc ************************/
    OtdrUpdateParamEstimateConcat(&OtdrStateCtrl, TestParam);

/*****************************************************************/
    OtdrPowerLevel = GetPowerLevelIndex(OtdrStateCtrl.MeasureParam.Lambda_nm);
    OtdrPowerLevel_LP = GetPowerLevelLPIndex(OtdrStateCtrl.MeasureParam.Lambda_nm);
    i  = GetMeasureLengthIndex(OtdrStateCtrl.MeasureParam.MeasureLength_m);
    j  = GetPulseWidthIndex(OtdrStateCtrl.MeasureParam.PulseWidth_ns);
    hr = OtdrReceiver[i][j];
    hp = OtdrPowerLevel[i][j];

    if((OtdrStateCtrl.MeasureParam.PulseWidth_ns >= 640) && \
            (OtdrStateCtrl.MeasureParam.MeasureLength_m >= 100000)){
        lr = OtdrReceiverLP[i-6][j-7];
        lp = OtdrPowerLevel_LP[i-6][j-7];
    }
    else{
        lr = hr;
        lp = hp;
    }

    ConcatParam.HighPower = hp;
    ConcatParam.HighRcv   = hr;
    ConcatParam.LowPower  = lp;
    ConcatParam.LowRcv    = lr;
    if(OtdrStateCtrl.ConcatParam.PowerMode != POWER_MODE_UNDEF)
    {
        ConcatParam.PowerMode = OtdrStateCtrl.ConcatParam.PowerMode;
        ConcatParam.ConcatPoint = 0;
        // specify the concat parameters
        memcpy(cp, &ConcatParam, sizeof(ConcatParam));
        return err_no;
    }

/*****************************************************************/
    printf("Start getConcat at time: %s\n", CurrentDateTime(timebuf));
    // memory alloc for SpiData and OtdrData
    err_no = SpiDataMalloc(&osd, OtdrStateCtrl.DataNum);
    err_no = OtdrDataMalloc(&OtdrData, OtdrStateCtrl.DataNum);
    if(err_no == E_MEM_ALLOC){
        return err_no;
    }

    apdv = getApdV(NULL);
/*************************** Start FPGA to get data ******************************/
    SpiClrData(&osd);
    OtdrClrData(&OtdrData);
    TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, hp, hr, apdv, measuretime);
    err_no = get_one_piece_of_data(device, &fsp, &osd, &OtdrStateCtrl, stop);
    if(err_no == E_CMD_OK){
        SpiCopyData(&OtdrData, &osd, SPIDATA_MASK_HP);
/**** check if it needs to save data ***************************/
        if(isSRDcode(&srd, CONCATMEASURE)){
            char  DATE_TIME[20], fn[100], param[64];
            time_t ct = time(NULL);
            struct tm *p = localtime(&ct);

            sprintf(DATE_TIME, "%04d%02d%02d_%02d%02d%02d", p->tm_year+1900, \
                                                            p->tm_mon+1, \
                                                            p->tm_mday, \
                                                            p->tm_hour, \
                                                            p->tm_min, \
                                                            p->tm_sec);
            sprintf(param, "%dnm%dkm%dns%dsP%dR%XV%d", OtdrStateCtrl.MeasureParam.Lambda_nm, \
                                                       OtdrStateCtrl.MeasureParam.MeasureLength_m/1000, \
                                                       OtdrStateCtrl.MeasureParam.PulseWidth_ns, \
                                                       OtdrStateCtrl.TotalMeasureTime/1000, \
                                                       hp, \
                                                       hr, \
                                                       apdv);
            sprintf(fn, "concat_%s_%s", DATE_TIME, param);
            putSRD(&srd, fn, param, CONCATMEASURE, OtdrData.hp, OtdrStateCtrl.DataNum);
        }
/*************************** analyse high power data *****************************/
        EstimateCurveConcat(&OtdrStateCtrl, &OtdrData, &ConcatParam);
        MeasureGetAutoPower(&OtdrStateCtrl, &ConcatParam, &OtdrData, &osd, apdv);
/*****************************************************************/
        // specify the concat parameters
        memcpy(cp, &ConcatParam, sizeof(ConcatParam));
    }

    SpiDataFree(&osd);
    OtdrDataFree(&OtdrData);
    writeSRD(&srd);
    return err_no;
}

/*
 ********************************************************************************
 *  函数名称： get_one_piece_of_data
 *  函数描述： get data from fpga, under specified parameters
 *  入口参数： 
 *  返回参数： E_CMD_OK, E_STOPPED, E_FPGA_ERR
 *  日期版本： 2016-10-21 10:57:28
 ********************************************************************************
*/
int get_one_piece_of_data(DeviceInfo_t *device, FpgaStartParam_t *fsp, OtdrSpiData_t *osd, OtdrStateCtrl_t *StateCtrl, int (*stop)(void))
{
    extern int wait_for_fpga_data(DeviceInfo_t *device, int waittime_ms, int (*stop)(void));
    int trytimes = 3, waittime = 0, sleeptime = 50*1000;  // check every 50ms
    int i, err_no = E_CMD_OK;
/************************************** start FPGA *****************************************/
    for(i = 0; i < trytimes; i++){
        // measuretime is fsp->sample_count in us
        FpgaStart(device, fsp);
        err_no = wait_for_fpga_data(device, fsp->measuretime, stop);

/******************************* copy data from FPGA to spidata ****************************************/
        if(err_no == E_CMD_OK){
            int datamask = SPIDATA_MASK_HP, add = 1;
            if(StateCtrl->TreatAsHighPowerData == 0)    datamask = SPIDATA_MASK_LP;
            if(StateCtrl->OtdrMode != OTDR_MODE_AVG)    add = 0;
            err_no = SpiGetDataFromFpga(device, osd, datamask, add);
            if(err_no == E_CMD_OK)  break;
        }
        else{
            m1_warning("get_one_piece_of_data failed, try again!\n");
            usleep(1000);
        }
    }
    return err_no;
}

/*
 **************************************************************************************************
 *  函数  名： getConnectState
 *  函数描述： Auto measure the fiberlen to adapt proper measurelength and pulsewidth
 *  入口参数： 
 *  返回参数： 1连接状态好，2连接状态差
 *  日期版本： 2016年 10月 20日 星期四 15:22:12 CST
 **************************************************************************************************
*/
int getConnectState(OtdrTestParam_t *tp, \
            int *state, \
            int (*stop)(void), \
            DeviceInfo_t *device)
{
    char timebuf[32], ss[2][8] = {"good", "bad"};
    int   i, j, apdv, cs;
	int   err_no = E_CMD_OK, measuretime = 400;
    int   rcv, power;
    FpgaStartParam_t fsp;
    
    OtdrSpiData_t   osd;
    OtdrData_t      OtdrData;
    OtdrStateCtrl_t OtdrStateCtrl;
    OtdrPowerLevel_t OtdrPowerLevel;

    SaveRunningData_t srd;
    parsePrint();
    configOtdrAlgo("/opt/TR600plus/config/otdralgo_config", tp->DataNum);
    parseSRDconf  ("/mnt/udisk/TR600plus/config/saverunningdata.conf", &srd);
/******************* Update Param and alloc ************************/
    assert((tp != NULL) && (state != NULL));
    OtdrUpdateParamConnectState(&OtdrStateCtrl, (OtdrTestParam_t *)tp);
/*****************************************************************/
    OtdrPowerLevel = GetPowerLevelIndex(OtdrStateCtrl.MeasureParam.Lambda_nm);
    i  = GetMeasureLengthIndex(OtdrStateCtrl.MeasureParam.MeasureLength_m);
    j  = GetPulseWidthIndex(OtdrStateCtrl.MeasureParam.PulseWidth_ns);
    rcv = OtdrReceiver[i][j];
    power = OtdrPowerLevel[i][j];

/*****************************************************************/
    printf("Start getConnectState at time: %s\n", CurrentDateTime(timebuf));
    // memory alloc for SpiData and OtdrData
    err_no = SpiDataMalloc(&osd, OtdrStateCtrl.DataNum);
    err_no = OtdrDataMalloc(&OtdrData, OtdrStateCtrl.DataNum);
    if(err_no == E_MEM_ALLOC){
        return err_no;
    }

    apdv = getApdV(NULL);
/*****************************************************************/
    SpiClrData(&osd);
    OtdrClrData(&OtdrData);
    TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, power, rcv, apdv, measuretime);
    err_no = get_one_piece_of_data(device, &fsp, &osd, &OtdrStateCtrl, stop);
    if(err_no == E_CMD_OK){
        SpiCopyData(&OtdrData, &osd, SPIDATA_MASK_HP);
/**** check if it needs to save data ***************************/
        if(isSRDcode(&srd, CONNECTSTATE)){
            char  DATE_TIME[20], fn[100], param[64];
            time_t ct = time(NULL);
            struct tm *p = localtime(&ct);

            sprintf(DATE_TIME, "%04d%02d%02d_%02d%02d%02d", p->tm_year+1900, \
                    p->tm_mon+1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
            sprintf(param, "%dnm-%dkm-%dns-%ds-P%d-R%X-V%d", OtdrStateCtrl.MeasureParam.Lambda_nm, \
                    OtdrStateCtrl.MeasureParam.MeasureLength_m/1000, OtdrStateCtrl.MeasureParam.PulseWidth_ns, \
                    OtdrStateCtrl.TotalMeasureTime/1000, power, rcv, apdv);
            sprintf(fn, "connectstate_%s_%s", DATE_TIME, param);
            putSRD(&srd, fn, param, CONNECTSTATE, OtdrData.hp, OtdrStateCtrl.DataNum);
        }
        OtdrStateCtrl.TotalMeasureTime = measuretime;
        cs = EstimateConnectState(&OtdrStateCtrl, &OtdrData);
        m1_print("getConnectState : %s\n", ss[cs-1]);
        *state = cs;
    }

    // free data
    SpiDataFree(&osd);
    OtdrDataFree(&OtdrData);
    writeSRD(&srd);
    return err_no;
}

/*
 ********************************************************************************
 *  函数名称 ： detectCommunicationLight
 *  函数描述 ： 通信光检测
 *  入口参数 ： 
 *  返回参数 ： 
 *  日期版本 ： 2016-11-08 17:32:27
 ********************************************************************************
*/
int detectCommunicationLight(DeviceInfo_t *device , \
            int *state)
{
#define TXLIGHT_NUM     64
    int i, sum, buf[TXLIGHT_NUM];
    FpgaStartParam_t fsp;

    SaveRunningData_t srd;
    parsePrint();
    parseSRDconf  ("/mnt/udisk/TR600plus/config/saverunningdata.conf", &srd);
	
    // 初始化fsp为特定参数
    fsp.lambda = 1550;
    fsp.pulsewidth = 10;
    fsp.sample_clk = 0x80;
    fsp.sample_num = 8;
    fsp.sample_count = 100;
    fsp.powerlevel = 4;
    fsp.rcv = 0x0A; // 通信光检测使用特定的跨阻
    fsp.apdv = getApdV(NULL);
    fsp.measuretime = 100;
    FpgaStart_sendTestParam(device, &fsp);

    for(i = 0; i < TXLIGHT_NUM; i++){
        buf[i] = getXLight(device);
        usleep(1000);
    }

    sum = 0;
    for(i = 0; i < TXLIGHT_NUM; i++){
        sum += buf[i];
    }
/**** check if it needs to save data ***************************/
    if(isSRDcode(&srd, TXLIGHT)){
        char  DATE_TIME[20], fn[40];
        time_t ct = time(NULL);
        struct tm *p = localtime(&ct);

        sprintf(DATE_TIME, "%04d%02d%02d_%02d%02d%02d", p->tm_year+1900, \
                p->tm_mon+1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
        sprintf(fn, "txlight_%s", DATE_TIME);
        putSRD(&srd, fn, NULL, TXLIGHT, buf, TXLIGHT_NUM);
    }
    writeSRD(&srd);

    *state = (sum > 32);
    return E_CMD_OK;
}

/*
 ********************************************************************************
 *  函数名称 ： lsaFitOTDRData  lsaFitAB
 *  函数描述 ： 使用最小二乘法将OTDR曲线的线段拟合成直线，保留事件点和噪声部分
 *              计算AB标杆区间的LSA损耗和衰减系数
 *  入口参数 ： 
 *  返回参数 ： 
 *  日期版本 ： 2016-11-04 09:09:47
 ********************************************************************************
*/
void lsaFitOTDRData(const OtdrTestResult_t *result, float *LinearOut)
{
    int i, ei, st, et;
    float k, b, mpp;

    if((result == NULL) || (LinearOut == NULL))             return;
    if((result->valid != 3) || (result->EventNum == 0))     return;

    mpp = C/(2*result->tp.n) / result->tp.SampleRate_Hz;

    // copy start Event
    for(i  = 0; i <= result->ep[0].EventEnd; i++){
        LinearOut[i] = result->Ai[i];
    }

    // copy Fiber and Event
    for(ei = 1; ei < result->EventNum; ei++){
        // get fiber range and LSA get attencoef
        st = result->ep[ei-1].EventEnd + 1;
        et = result->ep[ei].EventStart;
        LeastSquareMethod(result->Ai, st, et, &k, &b);
        k = -result->fs[ei-1].FiberSectionAttenCoef / 1000 / mpp;

        // fit fiber
        for(i = st; i <= et; i++){
            LinearOut[i] = k*i + b;
        }
        // copy Event
        for(i = et; i <= result->ep[ei].EventEnd; i++){
            LinearOut[i] = result->Ai[i];
        }
    }

    // copy from EndEvent to curve end
    for(i = result->ep[result->EventNum-1].EventEnd; i < result->tp.DataNum; i++){
        LinearOut[i] = result->Ai[i];
    }   
}

void lsaFitAB(const OtdrTestResult_t *result, int Apos, int Bpos, float *ABloss, float *ABcoef)
{
    extern Int32 InitEventGroupStart(const float AttenCoef[], EventGroupStart_t *EGS, Int32 EventNum);
    int i, j, ei, st, et, gs, ge;
    int eventsAB[MAX_EVENT_NUM], en, e1, e2;
	EventGroupStart_t   EGS;
    float k, b, ppm, *Ai, A1, A2, AC[MAX_EVENT_NUM];

    if((result == NULL) || (result->valid < 2)){
        *ABloss = RSVD_FLOAT;
        *ABcoef = RSVD_FLOAT;
        return;     // must have Ai, and Event is option
    }

    ppm = 2*result->tp.n * result->tp.SampleRate_Hz / C;

    st = MIN(Apos, Bpos);
    et = MAX(Apos, Bpos);

    if(st == et){
        *ABloss = 0;
        *ABcoef = 0;
        return;
    }

    if(result->valid > 2){
        // 重构事件组
        for(i = 0; i < result->FiberNum; i++){
            AC[i] = result->fs[i].FiberSectionAttenCoef;
        }
        InitEventGroupStart(AC, &EGS, result->EventNum);
    }
    else{
        EGS.EventGroupNum = 0;
    }

    // 判断AB之间是否存在事件组，即它们是否处于同一个光纤段中
    en = 0;
    for(ei = 0; ei < EGS.EventGroupNum; ei++){
        gs = EGS.EventGroupStart[ei];
        if(ei == EGS.EventGroupNum-1)   ge = result->EventNum-1;
        else                            ge = EGS.EventGroupStart[ei+1]-1;
        e1 = result->ep[gs].EventStart;
        e2 = result->ep[ge].EventEnd;
        if((e1 >= st) && (e1 <= et))    eventsAB[en++] = ei;
        else{
            if((e2 >= st) && (e2 <= et))    eventsAB[en++] = ei;
        }
    }

    if(en == 0)  Ai = result->Ai;
    else{
        Ai = (float*)malloc(result->tp.DataNum*sizeof(float));

        // copy curve from result to Ai
        for(i = st; i <= et; i++){
            Ai[i] = result->Ai[i];
        }

        // process events between A and B
        for(j = 0; j < en; j++){
            ei = eventsAB[j];
            gs = EGS.EventGroupStart[ei];
            if(ei == EGS.EventGroupNum-1)   ge = result->EventNum-1;
            else                            ge = EGS.EventGroupStart[ei+1]-1;

            e1 = result->ep[gs].EventStart;
            e2 = result->ep[ge].EventEnd;
            A1 = result->Ai[e1];
            A2 = result->Ai[e2];
            k = (A1 - A2) / (e1 - e2);
            b = A1 - k*e1;

            for(i = e1; i <= e2; i++){
                if((i >= st) && (i <= et)){
                    Ai[i] = k*i + b;
                }
            }
        }
    }

    // LSA
    LeastSquareMethod(Ai, st, et, &k, &b);
    *ABcoef = -k * ppm * 1000;
    *ABloss = -k * (et - st);
    
    if(en)  free(Ai);
}

void getTotalLoss(const OtdrTestResult_t *result, const int pos[], float loss[], int n)
{
    extern int ThreeArraySort(int *a1, int *a2, int *a3, int n);
    extern Int32 InitEventGroupStart(const float AttenCoef[], EventGroupStart_t *EGS, Int32 EventNum);

    int i, j, ei, st, gs1, gs2;
    int s1, s2;
	EventGroupStart_t   EGS;
    float mpp, AC[MAX_EVENT_NUM];

    if((result == NULL) || (result->valid < 3)){
        for(i = 0; i < n; i++)  loss[i] = RSVD_FLOAT;
        return;     // must have Ai, and Event is option
    }

    mpp = C / (2*result->tp.n*result->tp.SampleRate_Hz);

    // 重构事件组
    for(i = 0; i < result->FiberNum; i++){
        AC[i] = result->fs[i].FiberSectionAttenCoef;
    }
    InitEventGroupStart(AC, &EGS, result->EventNum);

    // 对于每个点，其累计损耗等于最接近它的事件组的累计损耗加上该光纤段引起的衰减
    for(i = 0; i < n; i++){
        st = pos[i];

        // 寻找该位置超过的第一个事件组的下标
        j = EGS.EventGroupNum-1;
        for(ei = 0; ei < EGS.EventGroupNum-1; ei++){
            gs1 = EGS.EventGroupStart[ei];
            gs2 = EGS.EventGroupStart[ei+1];

            s1 = result->ep[gs1].EventStart;
            s2 = result->ep[gs2].EventStart;
            if((st >= s1) && (st < s2)){
                j = ei;   
                break;
            }
        }

        if(j == EGS.EventGroupNum-1)    loss[i] = result->FiberLoss;
        else{
            gs1 = EGS.EventGroupStart[j];
            gs2 = EGS.EventGroupStart[j+1]-1;

            loss[i] = result->ep[gs1].EventTotalloss + AC[gs2] * (st - result->ep[gs1].EventStart) * mpp / 1000;
        }
    }
}

/*
 ********************************************************************************
 *  函数名称 ： specifiedTest
 *  函数描述 ： 启动特定参数测试
 *  入口参数 ： 
 *  返回参数 ： 
 *  日期版本 ： 2016-11-07 16:45:23
 ********************************************************************************
*/
int specifiedTest(OtdrSpTestParam_t *sp, \
            int (*ResultCopyBack)(OtdrSpTestResult_t *, OtdrSpTestResult_t *, int), \
            OtdrSpTestResult_t *OtdrSpTestResult, \
            DeviceInfo_t *device)

{
#define FPGA_PERIOD 1000    // starts fpga at every 1000ms
    char  timebuf[32];
	int   i, FpgaStartCount, err_no, TotalMeasureTime;

    OtdrSpiData_t   osd;
    OtdrData_t      OtdrData;
    FpgaStartParam_t fsp;

    OtdrSpTestResult_t result;
    OtdrStateCtrl_t OtdrStateCtrl;
    OtdrSpTestParam_t Stp, *TestParam = (OtdrSpTestParam_t *)sp;
	
    err_no = E_CMD_OK;
    printf("StartSpTest at time: %s\n", CurrentDateTime(timebuf));

    memcpy(&Stp, sp, sizeof(Stp));
    TestParam = &Stp;
    OtdrUpdateParamSpTest(&OtdrStateCtrl, TestParam);
    TestParam->SampleRate_Hz = OtdrStateCtrl.RealSampleRate_Hz;
/********************************************************************************/
/******************** Parameters OK, StartTest **********************************/
/********************************************************************************/
    // memory alloc for SpiData and OtdrData
    err_no = SpiDataMalloc(&osd, TestParam->DataNum);
    err_no = OtdrDataMalloc(&OtdrData, TestParam->DataNum);
    err_no = OtdrSpTestResultMalloc(&result, TestParam->DataNum);
    if(err_no == E_MEM_ALLOC){
        ResultCopyBack(NULL, NULL, err_no);
        return err_no;
    }
/***************************** Starts to Measure ***********************************/
    SpiClrData(&osd);
    OtdrClrData(&OtdrData);

    // Starts fpga to test every 1000ms
    FpgaStartCount = OtdrStateCtrl.MeasureParam.MeasureTime_ms / FPGA_PERIOD;
    TotalMeasureTime = 0;  // total measure time of this measure

    // 允许指定APD电压，也可以不指定
    if(TestParam->apdv == 0x7FFFFFFF)   TestParam->apdv = getApdV(NULL);
    for(i = 0; i < FpgaStartCount; i++)
    {
/****************************** StartFPGA to get data *********************************/
        TestParam2FpgaStartParam(&OtdrStateCtrl, &fsp, Stp.powerlevel, Stp.rcv, Stp.apdv, FPGA_PERIOD);
        err_no = get_one_piece_of_data(device, &fsp, &osd, &OtdrStateCtrl, NULL);
        if(err_no == E_CMD_OK)  TotalMeasureTime += FPGA_PERIOD;
        else    break;  // stopped
    }
/**************************** data ready, final process ****************************/
    if((err_no == E_CMD_OK) || (err_no == E_STOPPED)){
        SpiCopyData(&OtdrData, &osd, SPIDATA_MASK_HP);

        TestParam->MeasureTime_ms = TotalMeasureTime;
        m1_print("StartSpTest finished at time: %s\n", CurrentDateTime(timebuf));
        err_no = ProcessSpData(&OtdrData, &Stp, &result);
        ResultCopyBack(OtdrSpTestResult, &result, err_no);
    }

    // free data
    SpiDataFree(&osd);
    OtdrDataFree(&OtdrData);
    OtdrSpTestResultFree(&result);
    return err_no;
}

/*
 **************************************************************************************
 *    End    of    File
 **************************************************************************************
*/
